Skip to main content

第 8 课:模型微调 Llama Factory

两种模型微调的方式:Llama factoryverl,这里先介绍 Llama factory


(一)模型微调

1. 什么是模型微调

模型微调(Finetuning)是指:

在一个已经训练好的大模型(通常是大规模预训练语言模型,如 GPT、LLaMA、BERT)的基础上,再用一个特定领域的小数据集对其进行二次训练,从而让模型在保留原有通用能力的同时,适应特定任务或领域的需求。

你可以把预训练模型看成一个已经读完中学、大学的人,拥有很强的通识能力; 而微调就是让他在这个基础上接受某个专业的“职前培训”,

模型阶段类比举例
预训练读完通识课程GPT-3 预训练看了千亿token文本
微调定向实习/专业培训用医疗问答数据训练得到医疗 GPT 模型
指令微调接受任务型训练用 "问→答" 形式训练让模型听懂指令
RLHF上岗+反馈优化模型输出由人类偏好打分后再强化学习

预训练

  • 是指用海量通用语料(如网络文本、百科、小说、代码等)训练语言模型。这个阶段让模型学习语言结构、常识、推理能力等通用知识。

  • 举例:比如 GPT-3、LLaMA-2 都在这个阶段接触了数千亿 token 的文本,例如:

    “莎士比亚是谁?”、“Python 怎么写 for 循环?”、“华盛顿在哪一年就职?”

  • 此时的模型非常聪明,但不专精。就像大学毕业生一样,知识广,但没有专业经验。


微调(Fine-tuning)

  • 微调是在预训练模型的基础上,用某一类数据进一步训练,让模型掌握特定任务特定领域知识

  • 举例:你用几千条“医疗问答”训练模型,让它变成懂医疗的 GPT。

问:“血糖升高会引起什么后果?” → 模型回答得像医生。

  • 模型这时就像上过“医学实习”的大学生,虽然不是博士,但能给出合理答案。

指令微调(Instruction Tuning)

  • 是微调的一种,训练模型去理解“用户在给它发任务指令”,比如:

    • “请总结下面这段话。”

    • “将下面文字翻译成英文。”

    • “写一首诗。”

  • 举例:你训练模型理解:

{
  "instruction": "总结以下内容",
  "input": "人工智能是...",
  "output": "人工智能是一种模拟人类智能的技术。"
}

​ 模型变得“能听懂任务了”,能“看指令做事”,这就是你熟悉的 ChatGPT 风格的 LLM


RLHF(Reinforcement Learning with Human Feedback)

  • RLHF 是指:用人类偏好对模型输出打分(哪个回答更好、更自然、更安全),再用强化学习优化模型,让它输出更符合人类偏好的回答。

人类打分 → 训练出奖励模型 → 模型按“人类偏好”优化。

  • 给模型两个回答:

    • 回答 A:内容对,但语气生硬

    • 回答 B:内容对,语气自然

  • 我们打分说 B 更好。模型会被训练去更倾向生成 B 的风格


2. 什么情况下需要微调

应用场景是否需要微调?说明
你想让模型掌握行业知识(如法律、医疗)✅ 推荐微调通用模型知识不足
你要做一个特定功能的模型(如总结、翻译)✅ 推荐微调通用模型可能风格不一致
你有自己的语料库(如公司客服数据)✅ 推荐微调提高准确性和上下文理解
只想问通用问题,比如写作文/写代码❌ 不需要微调通用模型就足够
想用指令提示(prompt)就解决⚠️ 可用 prompt tuning轻量解决方案

3. 微调的原理

基础原理:

预训练语言模型的目标是最小化语言建模损失:

L=logP(xtx1,...,xt1) \mathcal{L} = -\sum \log P(x_t \mid x_1, ..., x_{t-1})

微调的目标是:最小化特定任务的数据损失,比如:

  • 问答:让模型在给定问题下给出正确回答;
  • 翻译:给定英文,让模型生成正确中文;
  • 总结:给定一段文章,模型输出总结。

这实际上就是“继续训练”,只不过是:

  • 使用小规模新数据;
  • 调整某部分模型参数(全量 or LoRA);
  • 学习方向是 “模型参数微调以适应任务”

4. 微调方式分类

微调类型参数量显存消耗是否修改原始模型参数适用场景
全量微调很多(全部)高精度任务,小模型
LoRA 微调否(添加低秩矩阵)大模型任务,低成本部署
Prefix Tuning极少很低多任务共用主模型
QLoRA非常少极低否,支持 4-bit 权重消耗极小的设备上训练

举例:你要训练一个“法律助手”

  • 通用 LLM 对于中文法律条文的理解可能模糊
  • 你准备了几千条法律问答(Q&A)
  • 使用 LLaMA Factory + LoRA 微调
  • 输出效果将更正式、精准、有条文引用

注意:之前说的 “指令微调(Instruction Tuning)” 的功能是定义训练数据的格式

典型格式为:

{
  "instruction": "翻译成英文",
  "input": "今天天气很好。",
  "output": "The weather is nice today."
}

这种格式可以用于:全量微调, LoRA 微调, Prefix Tuning, QLoRA 等等

也就是说:指令微调”是“要做的任务”,不是“怎么做的技术”✨


Lora 的数学原理

大型模型参数量巨大(几十亿),全量微调成本太高。每一层的参数都以矩阵形式计算和保存。

而我们观察到:在微调过程中,大多数参数的变化是低秩(low-rank)矩阵的扰动。

于是:我们不直接微调原始参数矩阵,而是:

让参数改变量用一个低秩矩阵近似表示,只训练这个小矩阵。

例如,假设你要微调的模型中有某个全连接层参数矩阵:

WRd×k \mathbf{W} \in \mathbb{R}^{d \times k}

LoRA 假设我们不直接更新 W\mathbf{W},而是引入两个小矩阵 ARr×k,BRd×r\mathbf{A} \in \mathbb{R}^{r \times k}, \mathbf{B} \in \mathbb{R}^{d \times r},只训练它们:

W~=W+ΔW=W+BA \tilde{\mathbf{W}} = \mathbf{W} + \Delta \mathbf{W} = \mathbf{W} + \mathbf{B}\mathbf{A}

其中 rd,kr \ll d, k,即:

  • 原始参数 WW:不变(冻结)
  • 改变量 ΔW=BA\Delta W = BA:可训练

输入为 xRk\mathbf{x} \in \mathbb{R}^k,原本输出:

y=Wx \mathbf{y} = \mathbf{W} \mathbf{x}

现在变为:

y=(W+BA)x=Wx+B(Ax) \mathbf{y} = (\mathbf{W} + \mathbf{B}\mathbf{A}) \mathbf{x} = \mathbf{W} \mathbf{x} + \mathbf{B}(\mathbf{A} \mathbf{x})

这就是 LoRA 的核心逻辑:“用一个小矩阵对原有模型输出进行可学习的扰动”


而训练的参数量的变化如下:

  • 原始层参数量:d×kd \times k
  • LoRA 参数量:r×k+d×r=r(k+d)r \times k + d \times r = r(k + d)

例如:取 d =4096, k = 4096, r =8,

则全量参数为 4096*4096 =16,777,216 , Lora 参数为 8*(4096+4096)=65,536,仅占 0.4%


QLoRA的数学原理

QLoRA 在Lora 的基础上,把模型主权重矩阵 W 不止做了冻结,还做了量化(Quantization),比如把 float16/full 变成了 4bit

在训练参数上,仍然是只训练插入的低秩矩阵

原因:

  • 语言模型的知识集中在主权重中,LoRA 插件负责微调输出即可;
  • 4-bit 权重足以表达主模型知识(推理保留);
  • LoRA 插件全保留浮点精度,保证梯度传播质量。
方法显存(LLaMA-7B, batch=1)
全量微调~25 GB
LoRA~11 GB
QLoRA~5.5 GB

Prefix tuning 的原理

Prefix Tuning 是一种参数高效微调方法,它不修改原始模型参数,也不训练权重矩阵,而是训练一小段“可学习的向量”,作为每一层注意力机制的“前缀”输入

本质:不动模型,只在前面加点引导提示(prefix)。

这是因为,大语言模型(如 Transformer)由多层自注意力模块组成,每一层的输入都会生成 Query、Key、Value 向量,然后做注意力计算:

Attention(Q,K,V)=softmax(QKdk)V \text{Attention}(Q, K, V) = \text{softmax}\left( \frac{QK^\top}{\sqrt{d_k}} \right)V

Prefix Tuning 的核心思路是:

  • 在每一层 attention 的输入中插入一些额外的“可学习向量”
  • 这些向量不是从输入文本计算来的,而是训练出来的参数(prefix embedding)
  • 最终影响 attention 输出,进而改变整个模型行为

数学原理

假设:

  • 原始输入序列是:x=(x1,x2,...,xn)\mathbf{x} = (x_1, x_2, ..., x_n)

  • 每层 attention 的输入为 query/key/value 向量:

    Q=XWQ,K=XWK,V=XWV Q = XW_Q, \quad K = XW_K, \quad V = XW_V

我们引入一个固定长度的 prefix token embedding 向量组(例如长度为 (p)):

PKRp×d,PVRp×d P_K \in \mathbb{R}^{p \times d}, \quad P_V \in \mathbb{R}^{p \times d}

它们是训练得到的、与输入无关的向量

然后我们在 attention 时,将 Key、Value 拼接为:

K=[PK K],V=[PV V] K' = \begin{bmatrix} P_K \ K \end{bmatrix}, \quad V' = \begin{bmatrix} P_V \ V \end{bmatrix}

而 Query 还是原样不变。

最终 attention 变成:

Attention(Q,K,V)=softmax(Q(K)d)V \text{Attention}(Q, K', V') = \text{softmax}\left( \frac{Q (K')^\top}{\sqrt{d}} \right)V'

📌 注意:

  • 模型主参数 (WQ,WK,WVW_Q, W_K, W_V) 都不变;
  • 只训练 prefix 向量 (PK,PVP_K, P_V);
  • 不影响模型结构,不改变 inference 流程(只是前面“加点料”);

5. 优势总结

优势说明
减少训练资源只训练少量参数或 adapter
保留预训练模型的能力不会忘掉原来学到的通用知识
提高特定任务性能快速适应新领域数据分布
可控性强你可以精准控制输出行为

微调 vs Prompt Engineering:全面对比

比较维度Prompt Engineering(提示工程)Fine-tuning(模型微调)
定义利用精心设计的提示词(prompt)引导已有模型回答在已有模型基础上继续训练,让其学会新任务
工具依赖无需改动模型,只改输入需要代码/训练框架(如 LLaMA Factory)
数据需求1~100 条也能生效通常需要 ≥ 数百条有标注的数据
资源消耗几乎为 0,推理时花时间显卡/CPU 时间、显存、存储资源需求大
调整难度上手简单,适合非技术用户需具备基本的 ML/代码能力
泛化能力利用原模型知识,很强若微调数据小且任务特殊,泛化能力可能下降
记忆能力无法“记住”新知识,重启后提示需重写微调后模型“记住”新能力,无需提示词
控制输出风格限制较多,靠 prompt 迂回控制可精确控制模型输出风格、术语等
多轮对话一致性复杂对话容易崩塌微调能强化长对话结构或角色设定
部署一致性无需部署新模型部署前需保存新模型(或 Adapter)
参数更新不修改模型参数会更新模型或部分参数(如 LoRA)
提升效果效果中等,有时 prompt 长反而出错效果明显,尤其在特定任务/领域表现优异

(二)Llama Factory

LLaMA Factory 是一个开源项目,旨在简化对 Hugging Face 上的 LLaMA 系列模型(包括 LLaMA、Baichuan、ChatGLM、Qwen、Mistral、DeepSeek 等)的微调(Fine-tuning)过程。

它支持以下多种微调策略:

  • Full fine-tuning
  • LoRA / QLoRA
  • Prefix Tuning / P-Tuning
  • Adapters

同时支持的数据格式:

  • ShareGPT, Alpaca, OASST1, HH-RLHF, UltraChat, Firefly, Belle, 自定义 JSON 等。

步骤一:安装 LLaMA Factory

git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory

pip install -r requirements.txt

如果你只需要基本的功能,可以精简依赖:

pip install transformers datasets peft accelerate

步骤二:数据准备

LLaMA Factory 支持多种数据格式,最推荐使用标准的 JSON 格式,示例如下:

[
  {
    "instruction": "给我写一首诗",
    "input": "关于春天的",
    "output": "春风又绿江南岸,明月何时照我还。"
  }
]

数据格式说明:

字段含义
instruction任务指令,如“翻译以下句子”
input可选输入(有些任务不需要)
output期望模型输出

步骤三:准备模型

llama-factory 的 Hugging Face Hub 下载你要微调的模型,比如:

huggingface-cli login
model_name_or_path = "meta-llama/Llama-2-7b-hf"

你也可以使用 Deepseek、ChatGLM、Baichuan、Qwen 等支持的模型。


步骤四:启动微调脚本

使用 CLI 启动微调任务:

python src/train_bash.py \
--stage sft \
--do_train \
--model_name_or_path path_or_name \
--dataset_dir data/ \
--dataset your_dataset_name.json \
--template default \
--finetuning_type lora \
--output_dir saves/your_model \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 8 \
--num_train_epochs 3 \
--learning_rate 5e-5 \
--fp16

🔧 你可以将上面命令写入 shell 脚本方便管理

常见参数解释:

参数说明
--stage sft微调阶段:sft(监督微调)
--finetuning_type loraLoRA 微调方式(支持 full、lora、qlora 等)
--templateprompt 模板,支持 default、chatglm、baichuan 等
--per_device_train_batch_size单卡批量大小
--gradient_accumulation_steps梯度累计步数
--fp16是否使用半精度训练(节省显存)

步骤五:模型使用

完成训练后,微调模型保存在 --output_dir 指定目录下。

加载方式如下:

from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

base_model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")

model = PeftModel.from_pretrained(base_model, "saves/your_model")

你可以使用提供的推理脚本 src/web_demo.py 来进行测试:

python src/web_demo.py \
--model_name_or_path meta-llama/Llama-2-7b-hf \
--adapter_path saves/your_model

这样会启动一个 Streamlit 的网页界面,可用于测试你的微调模型。

步骤内容
1安装依赖
2准备模型(LLaMA/Qwen等)
3准备数据集(Alpaca 格式等)
4配置微调命令(LoRA 推荐)
5启动训练
6使用推理脚本测试结果